/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年8月7日
 */
package com.massyframework.beanskin.runtime.framework.installing.fragment;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.commons.lang3.StringUtils;

import com.massyframework.beanskin.ModuleIdentifier;
import com.massyframework.beanskin.SimpleModuleIdentifier;
import com.massyframework.beanskin.util.version.Version;

/**
 * 片段Jar文件
 * @author huangkh
 *
 */
public class FragmentJarFile {
	public static final String SEPARATOR    =  "/";
	
	private JarFile jarFile;
	private Map<String, List<String>> namedMap;
	
	/**
	 * 
	 */
	public FragmentJarFile(JarFile jarFile) {
		this.jarFile = Objects.requireNonNull(jarFile, "\"jarFile\" cannot be null.");
		this.namedMap = new HashMap<>();
		this.scaning();
	}
	
	/**
	 * 提去下级文件及目录
	 * @param name {@link String},目录名
	 * @return {@link List}
	 */
	public List<String> getChildrens(String name){
		if (name == null) return Collections.emptyList();
		
		List<String> result = this.namedMap.get(name);
		return result == null ? Collections.emptyList() : result;
	}
	
	/**
	 * 判断{@code name}是否为目录名称
	 * @param name {@link String},名称
	 * @return {@link boolean}
	 */
	public boolean isDirectory(String name) {
		if (name == null) return false;
		
		if (this.namedMap.get(name) != null) {
			return true;
		}
		
		return false;
	}
	
	/**
	 * 按名称获取对应的输入流 
	 * @param name {@link String},名称
	 * @return {@link InputStream},可能返回null.
	 * @throws IOException 
	 */
	public InputStream getInputStream(String name) throws IOException {
		JarEntry entry = this.jarFile.getJarEntry(name);
		return entry == null ? null : this.jarFile.getInputStream(entry);
	}
	
	/**
	 * Jar文件
	 * @return
	 */
	public JarFile getJarFile() {
		return this.jarFile;
	}
	
	/**
	 * 提取所有模块标识符
	 * @return {@link List}
	 */
	public Set<ModuleIdentifier> getModuleIdentifiers() {
		Set<ModuleIdentifier> result = new HashSet<>();
		
		String name = "MODULE";
		List<String> groupIdNames = this.getChildrens(name);
		for (String groupIdName: groupIdNames) {
			String groupId = StringUtils.substring(groupIdName, name.length() + 1);
			List<String> moduleIdNames = this.namedMap.get(groupIdName);
			for (String moduleIdName: moduleIdNames) {
				String moduleId = StringUtils.substring(moduleIdName, groupIdName.length() + 1);
				List<String> versionNames = this.namedMap.get(moduleIdName);
				for (String versionName: versionNames) {
					String version = StringUtils.substring(versionName, moduleIdName.length() + 1);
					List<String> fileName = this.namedMap.get(versionName);
					
					String fragmentName = versionName.concat(SEPARATOR).concat("fragment.xml");
					if (fileName.stream().anyMatch( fn-> fn.equals(fragmentName))) {
						try {
							Version v = Version.parseVersion(version);
							
							ModuleIdentifier identifier =
									SimpleModuleIdentifier.newBuilder()
										.groupId(groupId)
										.moduleId(moduleId)
										.version(v)
										.build();
							result.add(identifier);
						}catch(Exception e) {
							continue;
						}
					}
				}
			}
		}
		
		return result;
	}
	
	protected void scaning() {
		Enumeration<JarEntry> em = this.jarFile.entries();
		while (em.hasMoreElements()) {
			JarEntry entry = em.nextElement();
			String name = entry.getName();
			if (entry.isDirectory()) {
				name = StringUtils.substring(name, 0, name.length() -1);
			}
			
			int pos = StringUtils.lastIndexOf(name, SEPARATOR);
			if (pos != -1) {
				String parent = StringUtils.substring(name, 0, pos);
				List<String> list = this.namedMap.computeIfAbsent(parent, key -> new ArrayList<>());
				list.add(name);
			}else {
				if (entry.isDirectory()) {
					this.namedMap.computeIfAbsent(name, key -> new ArrayList<>());
				}else {
					this.namedMap.put(name, null);
				}
			}
		}
	}
}
